package com.hanxiaozhang.concurrentmodel.diningphilosopher;

import java.util.concurrent.Semaphore;

/**
 * 〈一句话功能简述〉<br>
 * 〈哲学家进餐〉
 * <p>
 * https://blog.csdn.net/jackfrued/article/details/44499227
 *
 * @author hanxinghua
 * @create 2024/5/16
 * @since 1.0.0
 */
public class No1DiningPhilosopher {


    public static void main(String[] args) {

        // 5位哲学家的名字
        String[] names = {"店小二", "王大锤", "张三丰", "杨过", "李莫愁"};
//      ExecutorService es = Executors.newFixedThreadPool(No1DiningPhilosopher.NUM_OF_PHILO); // 创建固定大小的线程池
//      for(int i = 0, len = names.length; i < len; ++i) {
//          es.execute(new Philosopher(i, names[i]));   // 启动线程
//      }
//      es.shutdown();
        for (int i = 0, len = names.length; i < len; ++i) {
            new Thread(new Philosopher(i, names[i])).start();
        }
    }

    /**
     * 叉子数量(资源)
     */
    public static final int NUM_OF_FORKS = 5;

    /**
     * 哲学家数量(线程)
     */
    public static final int NUM_OF_PHILO = 5;

    /**
     * 叉子的信号量
     */
    public static Semaphore[] forks;

    /**
     * 哲学家的信号量
     */
    public static Semaphore counter;

    static {
        forks = new Semaphore[NUM_OF_FORKS];

        for (int i = 0, len = forks.length; i < len; ++i) {
            // 每个叉子的信号量为1
            forks[i] = new Semaphore(1);
        }

        // 如果有N个哲学家，最多只允许N-1人同时取叉子
        counter = new Semaphore(NUM_OF_PHILO - 1);
    }

    /**
     * 取得叉子
     *
     * @param index     第几个哲学家
     * @param leftFirst 是否先取得左边的叉子
     * @throws InterruptedException
     */
    public static void putOnFork(int index, boolean leftFirst) throws InterruptedException {
        if (leftFirst) {
            forks[index].acquire();
            forks[(index + 1) % NUM_OF_PHILO].acquire();
        } else {
            forks[(index + 1) % NUM_OF_PHILO].acquire();
            forks[index].acquire();
        }
    }

    /**
     * 放回叉子
     *
     * @param index     第几个哲学家
     * @param leftFirst 是否先放回左边的叉子
     * @throws InterruptedException
     */
    public static void putDownFork(int index, boolean leftFirst) throws InterruptedException {
        if (leftFirst) {
            forks[index].release();
            forks[(index + 1) % NUM_OF_PHILO].release();
        } else {
            forks[(index + 1) % NUM_OF_PHILO].release();
            forks[index].release();
        }
    }
}

/**
 * 哲学家
 */
class Philosopher implements Runnable {

    /**
     * 编号
     */
    private int index;

    /**
     * 名字
     */
    private String name;

    public Philosopher(int index, String name) {
        this.index = index;
        this.name = name;
    }

    @Override
    public void run() {
        while (true) {
            try {
                No1DiningPhilosopher.counter.acquire();
                boolean leftFirst = index % 2 == 0;
                No1DiningPhilosopher.putOnFork(index, leftFirst);
                // 取到两个叉子就可以进食
                System.out.println(name + "正在吃意大利面（通心粉）...");
                No1DiningPhilosopher.putDownFork(index, leftFirst);
                No1DiningPhilosopher.counter.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
